﻿ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
 using System.ServiceModel.Syndication;
 using System.ServiceModel.Web;
using System.Text.RegularExpressions;
 using System.Web;
 using SimpleServiceBus.EndpointManagement.Wcf.Contracts.Data;
using SimpleServiceBus.EndpointManagement.Wcf.Contracts.Data.Analytics;
using SimpleServiceBus.EndpointManagement.Wcf.Contracts.Operation;
using SimpleServiceBus.Persistence.Domain.Management.Entities;
using SimpleServiceBus.Persistence.Domain.Management.Repositories;

namespace SimpleServiceBus.EndpointManagement.Server.Wcf
{
    public class EndpointManagementWebService : IEndpointManagementWebService
    {
        private readonly IManagedEndpointRepository _endpointRepository;
        private readonly IEndpointErrorRepository _errorRepository;
        private readonly IEndpointPerformanceSummaryRepository _perfRepository;
        private readonly IContractMapper _mapper;

        //private readonly IQueryStringProvider _queryString;

        public EndpointManagementWebService(IManagedEndpointRepository endpointRepository, IEndpointErrorRepository errorRepository, IEndpointPerformanceSummaryRepository perfRepository, IContractMapper contractMapper)
        {
            _endpointRepository = endpointRepository;
            _errorRepository = errorRepository;
            _perfRepository = perfRepository;
            _mapper = contractMapper;
        }

        public ManagedEndpointContract GetEndpoint(string endpointId)
        {
            ManagedEndpoint endpoint = _endpointRepository.GetByEndpointId(endpointId);

            if (endpoint == null)
                HandleBadRequest(HttpStatusCode.NotFound, "An Endpoint with the ID " + endpointId + " does not exist.");

            return _mapper.Map<ManagedEndpoint,ManagedEndpointContract>(endpoint);
        }
       

        public List<ManagedEndpointContract> GetAllEndpoints()
        {
            IList<ManagedEndpoint> endpoints = _endpointRepository.FindAll();
            var converted = new List<ManagedEndpointContract>();
            foreach(ManagedEndpoint endpoint in endpoints)
                converted.Add(_mapper.Map<ManagedEndpoint,ManagedEndpointContract>(endpoint));
            return converted;
        }

        public SyndicationFeedFormatter GetAllEndpointsFeed(string format)
        {
            var items = new List<SyndicationItem>();
            IList<ManagedEndpoint> endpoints = _endpointRepository.FindAll();

            string baseUrl = "http://" + WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.Host];

            foreach (ManagedEndpoint endpoint in endpoints)
            {
                DateTimeOffset updated = endpoint.LastStatusUpdate.HasValue
                                             ? new DateTimeOffset(endpoint.LastStatusUpdate.Value.ToLocalTime())
                                             : DateTimeOffset.Now;

                string link = baseUrl + "/Endpoints/" + HttpUtility.UrlEncode(endpoint.EndpointID) + "/";
                string title = (endpoint.Name ??
                               endpoint.EndpointID) + " " + endpoint.Status + " @ " + updated.ToString("g");

                var item = new SyndicationItem(title,
                                               endpoint.StatusNote,
                                               new Uri(link),
                                               endpoint.EndpointID, updated);
                items.Add(item);
            }
            
            var feed = new SyndicationFeed("Simple Service Bus Endpoint Status", "",new Uri(baseUrl + "/Endpoints." + format),items);
            if (format.ToLower() == "atom")
                return new Atom10FeedFormatter(feed);
            return new Rss20FeedFormatter(feed);

        }

        public EndpointErrorContract GetEndpointError(string errorId)
        {
            int intErrorId;
            if (Int32.TryParse(errorId, out intErrorId))
            {
                EndpointError error = _errorRepository.Get(intErrorId);
                if (error != null)
                    return _mapper.Map<EndpointError,EndpointErrorContract>(error);
            }

            HandleBadRequest(HttpStatusCode.NotFound, "Endpoint Error " + errorId + " does not exist.");
            return null;
        }

        public List<EndpointErrorContract> GetEndpointErrorsByEndpointId(string endpointId,int maxResults)
        {
            ManagedEndpoint ep = _endpointRepository.GetByEndpointId(endpointId);

            if (ep == null)
                HandleBadRequest(HttpStatusCode.NotFound, "An Endpoint with the ID " + endpointId + " does not exist.");

            //DateTime? startDate = _queryString.GetQueryStringParameter<DateTime?>("startDate");
            //DateTime? endDate = _queryString.GetQueryStringParameter<DateTime?>("endDate");

            IList<EndpointError> found = _errorRepository.FindByEndpoint(ep,maxResults,null,null);
            var converted = new List<EndpointErrorContract>();
            foreach(EndpointError error in found)
                converted.Add(_mapper.Map<EndpointError,EndpointErrorContract>(error));
            return converted;
        }

        public List<EndpointErrorContract> GetEndpointErrors(int maxResults)
        {
            //DateTime? startDate = _queryString.GetQueryStringParameter<DateTime?>("startDate");
            //DateTime? endDate = _queryString.GetQueryStringParameter<DateTime?>("endDate");

            IList<EndpointError> found = _errorRepository.FindAll(maxResults,null,null);
            var converted = new List<EndpointErrorContract>();
            foreach (EndpointError error in found)
                converted.Add(_mapper.Map<EndpointError,EndpointErrorContract>(error));
            return converted;
        }

        public SyndicationFeedFormatter GetEndpointErrorsFeed(string format)
        {
            var items = new List<SyndicationItem>();
            IList<EndpointError> errors = _errorRepository.FindAll(25,DateTime.Today.AddMonths(-1), null);
            
            string baseUrl = "http://" + WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.Host];

            foreach (EndpointError error in errors)
            {
                string link = baseUrl + "/Errors/" + error.Id + "/";

                var item = new SyndicationItem((error.ManagedEndpoint.Name ?? error.ManagedEndpoint.EndpointID) + " @ " + error.Timestamp,
                                               error.ErrorMessage,
                                               new Uri(link),
                                               error.Id.ToString(), error.Timestamp);
                items.Add(item);
            }

            var feed = new SyndicationFeed("Simple Service Bus Error Log", "", new Uri(baseUrl + "/Errors." + format), items);
            if (format.ToLower() == "atom")
                return new Atom10FeedFormatter(feed);
            return new Rss20FeedFormatter(feed);

        }

        public List<EndpointPerformanceSummaryContract> GetEndpointPerformanceSummaries(string endpointId,int maxResults)
        {
            //DateTime? startDate = _queryString.GetQueryStringParameter<DateTime?>("startDate");
            //DateTime? endDate = _queryString.GetQueryStringParameter<DateTime?>("endDate");

            ManagedEndpoint ep = _endpointRepository.GetByEndpointId(endpointId);

            if (ep == null)
                HandleBadRequest(HttpStatusCode.NotFound, "An Endpoint with the ID " + endpointId + " does not exist.");

            IList<EndpointPerformanceSummary> found = _perfRepository.FindByEndpoint(ep,maxResults,null,null);
            var converted = new List<EndpointPerformanceSummaryContract>();
            foreach (EndpointPerformanceSummary summary in found)
                converted.Add(_mapper.Map<EndpointPerformanceSummary,EndpointPerformanceSummaryContract>(summary));
            return converted;
        }

        public TimeSeriesContract TimeSeriesQuery(string endpointId,string metricList, DateTime startTime, DateTime endTime)
        {
            ManagedEndpoint ep = _endpointRepository.GetByEndpointId(endpointId);

            if (ep == null)
                HandleBadRequest(HttpStatusCode.NotFound, "An Endpoint with the ID " + endpointId + " does not exist.");

            var results = new TimeSeriesContract
                              {
                                  Name = ep.Name ?? ep.EndpointID + " Performance Summary",
                                  XAxisName = "Time",
                                  YAxisName = "Data"
                              };

            if (String.IsNullOrEmpty(metricList))
                return new TimeSeriesContract();

            if (endTime == DateTime.MinValue)
                endTime = DateTime.Now;
            if (startTime == DateTime.MinValue)
                startTime = endTime.AddHours(-24);

            string[] metrics = ExpandMetricList(metricList, ep);

            results.DataSeries = new List<DataSeriesContract<DateTime, double>>();
            foreach(string metric in metrics)
            {
                
                var series = new DataSeriesContract<DateTime, double>
                                 {
                                     DataPoints = new List<DataPointContract<DateTime, double>>(),
                                     Name = metric
                                 };
                var summaries = _perfRepository.FindByEndpoint(ep, metric, startTime, endTime);
                foreach (EndpointPerformanceSummary summary in summaries)
                {
                    var point = new DataPointContract<DateTime, double>
                                    {
                                        X = new DataValueContract<DateTime>
                                                {
                                                    Caption = String.Empty,
                                                    Value = summary.SamplePeriodEndTime
                                                },
                                        Y = new DataValueContract<double>
                                                {
                                                    Caption = String.Empty,
                                                    Value = (metric.ToLower().EndsWith("max"))
                                                                ?
                                                                    summary.Max
                                                                : summary.Average
                                                }

                                    };
                    series.DataPoints.Add(point);
                }
                results.DataSeries.Add(series);

            }

            return results;

            
        }

        private string[] ExpandMetricList(string metricList,ManagedEndpoint endpoint)
        {
            if (!metricList.StartsWith("regex:"))
                return metricList.Split(',');
            var pattern = new Regex(metricList.Substring(6), RegexOptions.Compiled | RegexOptions.IgnoreCase);

            string[] candidates = _perfRepository.GetMetricTypesForEndpoint(endpoint);
            if (candidates == null || candidates.Length < 1)
                return new string[0];

            return candidates.Where(s => pattern.IsMatch(s)).ToArray();
        }

        private static void HandleBadRequest(HttpStatusCode statusCode, string description)
        {
            if (WebOperationContext.Current != null)
            {
                WebOperationContext.Current.OutgoingResponse.StatusDescription = description;
                WebOperationContext.Current.OutgoingResponse.StatusCode = statusCode;
                WebOperationContext.Current.OutgoingResponse.SuppressEntityBody = true;
            }
            throw new ArgumentException(description);
        }
    }
}
